package business.service.impl;

import base.pojo.Account;
import base.pojo.RealAuth;
import base.pojo.UserFile;
import base.pojo.UserInfo;
import base.query.CommonQuery;
import base.service.IAccountService;
import base.service.IRealAuthService;
import base.service.IUserFileService;
import base.service.IUserInfoService;
import business.mapper.BidMapper;
import business.mapper.BidRequestAuditHistoryMapper;
import business.mapper.BidRequestMapper;
import business.pojo.Bid;
import business.pojo.BidRequest;
import business.pojo.BidRequestAuditHistory;
import business.query.BidRequestQuery;
import business.service.IAccountFlowService;
import business.service.IBidRequestService;
import business.service.ISystemAccountService;
import business.util.CalculatetUtil;
import business.util.DecimalFormatUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import common.Const;
import common.ServerResponse;
import common.UserContext;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import util.BitOperationUtil;
import util.DateUtil;
import util.PageUtil;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class IBidRequestServiceImpl implements IBidRequestService
{
    @Autowired
    private BidMapper                       bidMapper                   ;
    @Autowired
    private BidRequestMapper                bidRequestMapper            ;
    @Autowired
    private IUserInfoService                iUserInfoService            ;
    @Autowired
    private IAccountService                 iAccountService             ;
    @Autowired
    private BidRequestAuditHistoryMapper    bidRequestAuditHistoryMapper;
    @Autowired
    private IRealAuthService                iRealAuthService            ;
    @Autowired
    private IUserFileService                iUserFileService            ;
    @Autowired
    private IAccountFlowService             iAccountFlowService         ;
    @Autowired
    private ISystemAccountService           iSystemAccountService       ;

    public ServerResponse update(BidRequest bidRequest)
    {
        if(bidRequestMapper.updateByPrimaryKey(bidRequest)  ==0 )
        {
            return ServerResponse.createByErrorMsg("乐观锁失败 bidRequest:" + bidRequest.getId());
        }
        return ServerResponse.createBySuccess();
    }

    public boolean canApplyBidRequest()
    {
        UserInfo userInfo = iUserInfoService.getCurrent();
        return             userInfo.getHasBaseInfo()
                        && userInfo.getHasRealAuth()
                        && userInfo.getHasVedioAuth()
                        && userInfo.getScore() >= Const.BASE_SCORE
                        && !userInfo.getHasBidRequest();

    }

    public void apply(BidRequest bidRequest)
    {
        Account account = iAccountService.getCurrent();
        if(canApplyBidRequest()
           &&   bidRequest.getBidRequestAmount().compareTo(Const.SMALLEST_BIDREQUEST_AMOUNT)>=0
           &&   bidRequest.getBidRequestAmount().compareTo(account.getRemainBorrowLimit())  <=0
           &&   bidRequest.getCurrentRate().compareTo(Const.SMALLEST_CURRENT_RATE)          >=0
           &&   bidRequest.getCurrentRate().compareTo(Const.MAX_CURRENT_RATE)               <=0
           &&   bidRequest.getMinBidAmount().compareTo(Const.SMALLEST_BID_AMOUNT)           >=0
          )
            {
                BidRequest newBidRequest = new BidRequest();
                newBidRequest.setBidRequestAmount(bidRequest.getBidRequestAmount())     ;
                newBidRequest.setCurrentRate(bidRequest.getCurrentRate())               ;
                newBidRequest.setDescription(bidRequest.getDescription())               ;
                newBidRequest.setDescription(bidRequest.getDescription())               ;
                newBidRequest.setDisableDays(bidRequest.getDisableDays())               ;
                newBidRequest.setMinBidAmount(bidRequest.getMinBidAmount())             ;
                newBidRequest.setMonthsReturn(bidRequest.getMonthsReturn())             ;
                newBidRequest.setReturnType(bidRequest.getReturnType())                 ;
                newBidRequest.setTitle(bidRequest.getTitle())                           ;
                //-------------
                newBidRequest.setApplyTime(new Date())                                  ;
                newBidRequest.setBidRequestState(Const.BIDREQUEST_STATE_PUBLISH_PENDING);
                newBidRequest.setCreateUser(UserContext.getCurrentUser())               ;
                newBidRequest.setTotalRewardAmount(CalculatetUtil.calTotalInterest(
                        newBidRequest.getReturnType()       ,
                        newBidRequest.getBidRequestAmount() ,
                        newBidRequest.getCurrentRate()      ,
                        newBidRequest.getMonthsReturn()
                ));
                bidRequestMapper.insert(newBidRequest)           ;
                UserInfo userInfo = iUserInfoService.getCurrent();
                userInfo.addState(Const.OP_HAS_BIDREQUEST)       ;
                try {
                    iUserInfoService.update(userInfo)            ;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

    }

    public ServerResponse query(BidRequestQuery bidRequestQuery)
    {
        return ServerResponse.createBySuccess(bidRequestMapper.query(bidRequestQuery));
    }

    public ServerResponse getBidAgoAudit(Long id)
    {
        return ServerResponse.createBySuccess(bidRequestMapper.selectByPrimaryKey(id));
    }

    public BidRequest getBidRequest(Long id)
    {
        return bidRequestMapper.selectByPrimaryKey(id);
    }

    public ServerResponse bidAgoAudit(Long id,String remark,int state)
    {
        BidRequest old = bidRequestMapper.selectByPrimaryKey(id)                            ;
        if(old != null && old.getBidRequestState() == Const.BIDREQUEST_STATE_PUBLISH_PENDING)
        {
            BidRequestAuditHistory history = new BidRequestAuditHistory()                   ;
            history.setApplier(old.getCreateUser())                                         ;
            history.setApplyTime(old.getApplyTime())                                        ;
            history.setAuditType(BidRequestAuditHistory.PUBLISH_AUDIT)                      ;
            history.setAuditor(UserContext.getCurrentUser())                                ;
            history.setAuditTime(new Date())                                                ;
            history.setRemark(remark)                                                       ;
            history.setState(state)                                                         ;
            history.setBinRequestId(old.getId())                                            ;
            bidRequestAuditHistoryMapper.insert(history)                                    ;
            if(state == BidRequestAuditHistory.STATE_AUDIT)
            {
                old.setBidRequestState(Const.BIDREQUEST_STATE_BIDDING)                      ;
                old.setNote(remark)                                                         ;
                old.setPublishTime(new Date())                                              ;
                old.setDisableDate(DateUtils.addDays(new Date(),old.getDisableDays()))      ;
            }else
                {
                    old.setBidRequestState(Const.BIDREQUEST_STATE_PUBLISH_REFUSE)           ;
                    UserInfo userInfo = iUserInfoService.get(old.getCreateUser().getId())   ;
                    userInfo.removeState(Const.OP_HAS_BIDREQUEST)                           ;
                    try {
                        iUserInfoService.update(userInfo)                                   ;
                    } catch (Exception e) {
                        e.printStackTrace()                                                 ;
                    }
                }
            update(old)                                                                     ;
            return ServerResponse.createBySuccess()                                         ;
        }
        return ServerResponse.createByError()                                               ;
    }

    public ServerResponse borrowInfo(Long id)
    {
        BidRequest br = getBidRequest(id);
        if(br != null)
        {
            UserInfo u = iUserInfoService.get(br.getCreateUser().getId())           ;
            List<BidRequestAuditHistory> bhlist = listAuditHistoryByBidRequest(id)  ;
            RealAuth realAuth = iRealAuthService.getRealAuth(u.getRealAuthId())     ;
            List<UserFile> uflist = iUserFileService.listFilesByAudit(u.getId())    ;
            Map<String,Object> map = new HashMap<>()                                ;
            map.put("userInfo",u)                                                   ;
            map.put("bidRequestAuditHistory",bhlist)                                ;
            map.put("realAuth",realAuth)                                            ;
            map.put("userFiles",uflist)                                             ;
            map.put("bidRequest",br)                                             ;
            return ServerResponse.createBySuccess(map)                              ;
        }                                                                           
        return ServerResponse.createByError()                                       ;
    }

    public List<BidRequestAuditHistory> listAuditHistoryByBidRequest(Long id)
    {
        return  bidRequestAuditHistoryMapper.listByBidRequest(id);
    }

    public ServerResponse listAll(BidRequestQuery bidRequestQuery)
    {
        PageHelper.startPage(bidRequestQuery.getPageNum(),bidRequestQuery.getPageSize());
        PageHelper.orderBy("bidRequestState ASC")                                       ;
        List<BidRequest> list = bidRequestMapper.selectAll(bidRequestQuery)             ;
        return ServerResponse.createBySuccess(PageUtil.returnPageInfo(list))            ;
    }

    /**
     * 投标
     */
    public ServerResponse bid(Long bidRequestId, BigDecimal amount) throws Exception {
        Account currentAccount = iAccountService.getCurrent()                           ;
        BidRequest bidRequest = getBidRequest(bidRequestId)                             ;
        if(bidRequest   != null
                        &&  bidRequest.getBidRequestState() == Const.BIDREQUEST_STATE_BIDDING
                        &&  !bidRequest.getCreateUser().getId().equals(UserContext.getCurrentUser().getId())
                        &&  currentAccount.getUsableAmount().compareTo(amount)>=0
                        &&  amount.compareTo(bidRequest.getMinBidAmount())>=0
                        &&  amount.compareTo(bidRequest.getRemainAmount())<=0
           )
        {
            Bid bid =new Bid()                                                                  ;
            bid.setActualRate(bidRequest.getCurrentRate())                                      ;
            bid.setAvailableAmount(amount)                                                      ;
            bid.setBidRequestId(bidRequest.getId())                                             ;
            bid.setBidRequestTitle(bidRequest.getTitle())                                       ;
            bid.setBidTime(new Date())                                                          ;
            bid.setBidUser(UserContext.getCurrentUser())                                        ;
            bidMapper.insert(bid)                                                               ;
            currentAccount.setUsableAmount(currentAccount.getUsableAmount().subtract(amount))   ;
            currentAccount.setFreezedAmount(currentAccount.getFreezedAmount().add(amount))      ;
            iAccountFlowService.bid(bid,currentAccount)                                         ;
            bidRequest.setBidCount(bidRequest.getBidCount() + 1)                                ;
            bidRequest.setCurrentSum(bidRequest.getCurrentSum().add(amount))                    ;
            if(bidRequest.getBidRequestAmount().equals(bidRequest.getCurrentSum()))
            {//标已投满,进入满标一审
                bidRequest.setBidRequestState(Const.BIDREQUEST_STATE_APPROVE_PENDING_ONE)       ;
            }
            iAccountService.update(currentAccount)                                              ;
            update(bidRequest)                                                                  ;
            return ServerResponse.createBySuccess()                                             ;
        }
        return ServerResponse.createByError()                                                   ;
    }

    public ServerResponse bidFullOneAudit(Long id, String remark, int state) throws Exception {
        BidRequest old = bidRequestMapper.selectByPrimaryKey(id)                            ;
        if(old != null && old.getBidRequestState() == Const.BIDREQUEST_STATE_APPROVE_PENDING_ONE)
        {
            BidRequestAuditHistory history = new BidRequestAuditHistory();
            history.setApplier(old.getCreateUser());
            history.setApplyTime(new Date());
            history.setAuditor(UserContext.getCurrentUser());
            history.setBinRequestId(old.getId());
            history.setAuditTime(new Date());
            history.setAuditType(BidRequestAuditHistory.FULL_AUDIT_ONE);
            history.setRemark(remark);
            history.setState(state);
            bidRequestAuditHistoryMapper.insert(history);
            if(state == BidRequestAuditHistory.STATE_AUDIT)
            {//进入满标二审
                old.setBidRequestState(Const.BIDREQUEST_STATE_APPROVE_PENDING_TWO);
            }else
                {
                    old.setBidRequestState(Const.BIDREQUEST_STATE_REJECTED) ;
                    returnMoney(old);
                    UserInfo borrowUser = iUserInfoService.get(old.getCreateUser().getId());
                    borrowUser.removeState(Const.OP_HAS_BIDREQUEST);
                    iUserInfoService.update(borrowUser);
                }
            update(old);
            return ServerResponse.createBySuccess();
        }
        return ServerResponse.createByError();
    }

    /**
     * 退钱
     */
    private void returnMoney(BidRequest old) throws Exception {
      for(Bid bid : old.getBids())
      {
          Account bidAccount = iAccountService.get(bid.getBidUser().getId())                            ;
          bidAccount.setUsableAmount(bidAccount.getUsableAmount().add(bid.getAvailableAmount()))        ;
          bidAccount.setFreezedAmount(bidAccount.getFreezedAmount().subtract(bid.getAvailableAmount())) ;
          iAccountFlowService.returnMoney(bid,bidAccount)                                               ;
          iAccountService.update(bidAccount)                                                            ;
      }
    }

    public ServerResponse bidFullTwoAudit(Long id, String remark, int state) throws Exception {
        BidRequest old = bidRequestMapper.selectByPrimaryKey(id)                            ;
        if(old != null && old.getBidRequestState() == Const.BIDREQUEST_STATE_APPROVE_PENDING_TWO)
        {
            BidRequestAuditHistory history = new BidRequestAuditHistory();
            history.setApplier(old.getCreateUser());
            history.setApplyTime(new Date());
            history.setAuditor(UserContext.getCurrentUser());
            history.setBinRequestId(old.getId());
            history.setAuditTime(new Date());
            history.setAuditType(BidRequestAuditHistory.FULL_AUDIT_TWO);
            history.setRemark(remark);
            history.setState(state);
            bidRequestAuditHistoryMapper.insert(history);
            if(state == BidRequestAuditHistory.STATE_AUDIT)
            {
                old.setBidRequestState(Const.BIDREQUEST_STATE_PAYING_BACK);
                Account borrowAccount = iAccountService.get(old.getCreateUser().getId());
                borrowAccount.setUsableAmount(borrowAccount.getUsableAmount().add(old.getBidRequestAmount()));
                iAccountFlowService.borrowSuccess(old,borrowAccount);
                borrowAccount.setUnReturnAmount(borrowAccount.getUnReturnAmount()
                        .add(old.getBidRequestAmount()).add(old.getTotalRewardAmount()));
                borrowAccount.setRemainBorrowLimit(borrowAccount.getRemainBorrowLimit()
                        .subtract(old.getBidRequestAmount()));
                UserInfo borrowUser = iUserInfoService.get(old.getCreateUser().getId());
                borrowUser.removeState(Const.OP_HAS_BIDREQUEST);
                iUserInfoService.update(borrowUser);
                BigDecimal manageChargeFee = CalculatetUtil
                        .calAccountManagementCharge(old.getBidRequestAmount());
                borrowAccount.setUsableAmount(borrowAccount.getUsableAmount()
                        .subtract(manageChargeFee));
                iAccountFlowService.borrowChargeFee(manageChargeFee, old,
                        borrowAccount);
                iSystemAccountService.chargeBorrowFee(old, manageChargeFee);
                Map<Long, Account> updates = new HashMap<>();
                BigDecimal totalBidInterest = Const.Zero;
                for (int i = 1; i <= old.getBids().size(); i++) {
                    Bid bid = old.getBids().get(i - 1);
                    Long bidUserId = bid.getBidUser().getId();
                    Account bidAccount = updates.get(bidUserId);
                    if (bidAccount == null) {
                        bidAccount = iAccountService.get(bidUserId);
                        updates.put(bidUserId, bidAccount);
                    }
                    bidAccount.setFreezedAmount(bidAccount.getFreezedAmount()
                            .subtract(bid.getAvailableAmount()));
                    iAccountFlowService.bidSuccess(bid, bidAccount);
                    bidAccount.setUnReceivePrincipal(bidAccount
                            .getUnReceivePrincipal().add(
                                    bid.getAvailableAmount()));
                    BigDecimal bidInterest = Const.Zero;
                    if (i < old.getBids().size()) {
                        bidInterest = bid
                                .getAvailableAmount()
                                .divide(old.getBidRequestAmount(),
                                        Const.CAL_SCALE,
                                        RoundingMode.HALF_UP)
                                .multiply(old.getTotalRewardAmount());

                        bidInterest = DecimalFormatUtil.formatBigDecimal(
                                bidInterest, Const.STORE_SCALE);
                        // 累加
                        totalBidInterest = totalBidInterest.add(bidInterest);
                    } else {
                        bidInterest = old.getTotalRewardAmount().subtract(
                                totalBidInterest);
                    }
                    bidAccount.setUnReceiveInterest(bidAccount
                            .getUnReceiveInterest().add(bidInterest));
                }
                iAccountService.update(borrowAccount);
                for (Account account : updates.values()) {
                    iAccountService.update(account);
                }
            }else
            {
                old.setBidRequestState(Const.BIDREQUEST_STATE_REJECTED) ;
                returnMoney(old);
                UserInfo borrowUser = iUserInfoService.get(old.getCreateUser().getId());
                borrowUser.removeState(Const.OP_HAS_BIDREQUEST);
                iUserInfoService.update(borrowUser);
            }
            update(old);
            return ServerResponse.createBySuccess();
        }
        return null;
    }

}